package com.qiuzhi.spring;

import com.qiuzhi.service.WebLogAspect;
import com.qiuzhi.spring.annotations.*;
import jdk.nashorn.internal.runtime.regexp.joni.CodeRangeBuffer;

import java.io.*;
import java.lang.reflect.*;
import java.net.URL;
import java.net.URLDecoder;
import java.sql.SQLOutput;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class QiuzhiApplicationContext {

    private Class<?> configClass;

    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();//单例池 一级缓存
    private Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();//二级缓存 用来解决循环依赖
    private Map<String, Object> proxyObjects = new ConcurrentHashMap<>();//三级缓存 存储proxy

    private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();//存储bean的解释信息

    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    public QiuzhiApplicationContext(Class<?> configClass) {
        this.configClass = configClass;
        //解析配置类-->扫描包
        scan(configClass);

        //初始化所有单例bean
        for (String beanName : beanDefinitionMap.keySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if ("singleton".equals(beanDefinition.getScope())) {
                Object bean = createBean(beanDefinition);
                singletonObjects.put(beanName, bean);
            }
        }

//        singletonObjects.forEach((k, v) -> System.out.println(k + ":" + v));
//        System.out.println("----------------------------------------");
        // proxyObjects.forEach((k, v) -> System.out.println(k + ":" + v));
    }

    private void scan(Class<?> configClass) {
        ComponentScan annotationComponentScan = configClass.getDeclaredAnnotation(ComponentScan.class);
        //包名
        String packageName = annotationComponentScan.value();

        ClassLoader classLoader = QiuzhiApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(packageName.replace(".", "/"));

        try {
            String filePath = URLDecoder.decode(resource.getPath(), "utf-8");
            File file = new File(filePath);
            if (file.isDirectory()) {
                //拿到CompenentScan扫描包下的所有class文件
                File[] files = file.listFiles();
                for (File f : files) {
                    //获取包下面的所有的class文件
                    if (f.getName().endsWith(".class")) {
                        String className = packageName + "." + f.getName().replace(".class", "");
                        Class<?> clazz = classLoader.loadClass(className);
                        //如果是被spring管理的类
                        if (clazz.isAnnotationPresent(Component.class)) {
                            //如果是BeanPostProcessor对象
                            if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                BeanPostProcessor beanpostProcess = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                                beanPostProcessorList.add(beanpostProcess);
                            }
                            //获取beanName
                            Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();
                            //创建bean解释对象
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setClazz(clazz);

                            //如果是代理对象
                            if (clazz.isAnnotationPresent(Aspect.class)) {
                                beanName = clazz.getDeclaredAnnotation(Aspect.class).value();
                                //代理对象
                                Object proxy = createBean(beanDefinition);
                                proxyObjects.put(beanName, proxy);
                            } else {
                                //如果加了Scope单例注解
                                if (clazz.isAnnotationPresent(Scope.class)) {
                                    Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                                    beanDefinition.setScope(scopeAnnotation.value());
                                } else {
                                    //没有就默认是单例模式
                                    beanDefinition.setScope("singleton");
                                }
                                beanDefinitionMap.put(beanName, beanDefinition);
                            }
                        }

                    }

                }
            }

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public Object getBean(String beanName) {
        //如果bean的解释信息不存在，那么就无法创建bean
        if (!beanDefinitionMap.containsKey(beanName)) {
            throw new NullPointerException("不存在对应的bean");
        }

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        //如果是单例模式 && 该对象存在
        if ("singleton".equals(beanDefinition.getScope())) {
            if (singletonObjects.containsKey(beanName)) {
                Object bean = singletonObjects.get(beanName);
                return bean;
            }
        }
        if (earlySingletonObjects.containsKey(beanName)) {
            Object bean = earlySingletonObjects.get(beanName);
            return bean;
        }
        //如果不是单例模式
        Object bean = createBean(beanDefinition);
        return bean;

    }

    private Object createBean(BeanDefinition beanDefinition) {
        Object instance = null;
        try {
            Class<?> clazz = beanDefinition.getClazz();
            instance = clazz.getDeclaredConstructor().newInstance();
            String beanName = null;
            if (clazz.isAnnotationPresent(Component.class)) {
                beanName = clazz.getAnnotation(Component.class).value();
                earlySingletonObjects.put(beanName, instance);
            }

            //字段依赖注入
            for (Field field : clazz.getDeclaredFields()) {
                //如果被Autowired修饰，那就说明要依赖注入
                if (field.isAnnotationPresent(Autowired.class)) {
                    Object bean = getBean(field.getName());
                    field.setAccessible(true);
                    field.set(instance, bean);
                }
            }
            //删除缓存
            earlySingletonObjects.remove(beanName);

            //初始化之前
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
            }

            //设置属性
            if (instance instanceof InitializingBean) {
                ((InitializingBean) instance).afterPropertiesSet();
            }

            //初始化之后
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
            }

            //动态代理
            instance = proxy(instance, beanName);

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return instance;
    }

    private Object proxy(Object bean, String beanName) {
        //匹配
        if (proxyObjects.containsKey(beanName)) {
            InvocationHandler invoke = (InvocationHandler) proxyObjects.get(beanName);
            Object proxyBean = Proxy.newProxyInstance(QiuzhiBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), invoke);
            return proxyBean;
        }
        return bean;
    }
}
